// Copyright (c) 2022 MASSA LABS <info@massa.net>

use massa_deferred_calls::DeferredCall;
use massa_final_state::StateChanges;
use massa_models::{
    address::Address, amount::Amount, block_id::BlockId, deferred_calls::DeferredCallId,
    operation::OperationId, output_event::SCOutputEvent, slot::Slot,
};
use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, fmt::Display};

/// The result of the read-only execution.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ReadOnlyResult {
    /// An error occurred during execution.
    Error(String),
    /// The result of a successful execution.
    Ok(Vec<u8>),
}

/// The response to a request for a read-only execution.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ExecuteReadOnlyResponse {
    /// The slot at which the read-only execution occurred.
    pub executed_at: Slot,
    /// The result of the read-only execution.
    pub result: ReadOnlyResult,
    /// The output events generated by the read-only execution.
    pub output_events: VecDeque<SCOutputEvent>,
    /// The gas cost for the execution
    pub gas_cost: u64,
    /// state changes caused by the execution step
    pub state_changes: StateChanges,
}

impl Display for ExecuteReadOnlyResponse {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "Executed at slot: {}", self.executed_at)?;
        writeln!(
            f,
            "Result: {}",
            match &self.result {
                ReadOnlyResult::Error(e) =>
                    format!("an error occurred during the execution: {}", e),
                ReadOnlyResult::Ok(ret) => format!("success, returned value: {:?}", ret),
            }
        )?;
        writeln!(f, "Gas cost: {}", self.gas_cost)?;
        if !self.output_events.is_empty() {
            writeln!(f, "Generated events:",)?;
            for event in self.output_events.iter() {
                writeln!(f, "{}", event)?; // id already displayed in event
            }
        }
        Ok(())
    }
}

/// read only bytecode execution request
#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct ReadOnlyBytecodeExecution {
    /// max available gas
    pub max_gas: u64,
    /// byte code
    pub bytecode: Vec<u8>,
    /// caller's address, optional
    pub address: Option<Address>,
    /// Operation datastore, optional
    pub operation_datastore: Option<Vec<u8>>,
    /// fee
    pub fee: Option<Amount>,
}

/// read SC call request
#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct ReadOnlyCall {
    /// max available gas
    pub max_gas: u64,
    /// target address
    pub target_address: Address,
    /// target function
    pub target_function: String,
    /// function parameter
    pub parameter: Vec<u8>,
    /// caller's address, optional
    pub caller_address: Option<Address>,
    /// coins
    pub coins: Option<Amount>,
    /// fee
    pub fee: Option<Amount>,
}

/// Context of the transfer
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TransferContext {
    #[serde(rename = "operation_id")]
    /// Transfer made in an operation
    Operation(OperationId),
    #[serde(rename = "asc_index")]
    /// Transfer made in an asynchronous call
    ASC(u64),
    #[serde(rename = "deferred_call_id")]
    /// Transfer made in a deferred call
    DeferredCall(DeferredCallId),
}

/// Structure defining a transfer
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Transfer {
    /// The sender of the transfer
    pub from: Address,
    /// The receiver of the transfer
    pub to: Address,
    /// The amount of the transfer
    pub amount: Amount,
    /// The effective amount received by the receiver
    pub effective_amount_received: Amount,
    /// If the transfer succeed or not
    pub succeed: bool,
    /// Fee
    pub fee: Amount,
    /// Block ID
    pub block_id: BlockId,
    /// Context
    pub context: TransferContext,
}

/// request for deferred call quote
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DeferredCallsQuoteRequest {
    /// The slot at which the deferred call is to be executed.
    pub target_slot: Slot,
    /// The maximum gas requested.
    pub max_gas_request: u64,
    /// Size of parameters
    pub params_size: u64,
}

/// The response to a request for a deferred call quote.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DeferredCallsQuoteResponse {
    /// The slot at which the deferred call is to be executed.
    pub target_slot: Slot,
    /// The maximum gas requested.
    pub max_gas_request: u64,
    /// if the slot is bookable
    pub available: bool,
    /// the cost for booking the call
    pub price: Amount,
}

/// response for deferred call
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DeferredCallResponse {
    /// deferred call id
    pub call_id: String,
    /// deferred call
    pub call: DeferredCall,
}

/// response for deferred calls by slot
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DeferredCallsSlotResponse {
    /// deferred calls
    pub slot: Slot,
    /// deferred calls
    pub call_ids: Vec<String>,
}
